package org.fhawk.tinyspring.aop.proxy;

import net.sf.cglib.core.ClassGenerator;
import net.sf.cglib.core.DefaultGeneratorStrategy;
import net.sf.cglib.core.GeneratorStrategy;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import net.sf.cglib.transform.TransformingClassGenerator;
import net.sf.cglib.transform.impl.AddPropertyTransformer;
import org.fhawk.tinyspring.exception.ProxyException;
import org.fhawk.tinyspring.ioc.BeanHelper;
import org.fhawk.tinyspring.ioc.IocHelper;
import org.fhawk.tinyspring.ioc.annotation.inject;
import org.objectweb.asm.Type;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

public class CglibProxyManager implements MethodInterceptor {
    private final Logger logger = LoggerFactory.getLogger(CglibProxyManager.class);
    private Enhancer enhancer = new Enhancer();
    private List<? extends Proxy> proxies;

    public Object newInstance(Class<?> target, List<Class<? extends Proxy>> proxyList) {
        enhancer.setSuperclass(target);
        enhancer.setCallback(this);
        enhancer.setStrategy(initStrateg(target));
        proxies = proxyList.stream().map(clazz -> {
            try {
                return clazz.newInstance();
            } catch (Exception e) {
                logger.error("获取类实例失败。", e);
            }
            return null;
        }).filter(Objects::nonNull).collect(Collectors.toList());
        return enhancer.create();
    }

    private GeneratorStrategy initStrateg(Class<?> target) {
        List<Field> fields = Arrays.stream(target.getDeclaredFields()).filter(field -> field.isAnnotationPresent(inject.class)).collect(Collectors.toList());
        String[] names = new String[fields.size()];
        Type[] types = new Type[fields.size()];
        for (int i = 0; i < fields.size(); i++) {
            names[i] = fields.get(i).getName();
            types[i] = Type.getType(fields.get(i).getType());
        }
        return new DefaultGeneratorStrategy() {
            @Override
            protected ClassGenerator transform(ClassGenerator cg) {
                return new TransformingClassGenerator(cg,
                    new AddPropertyTransformer(names, types));
            }
        };
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        Object result = null;
        try {
            proxies.forEach(proxy -> {
                try {
                    proxy.doBefore(method);
                } catch (ProxyException e) {
                    logger.error("代理前置处理失败", e);
                }
            });
            injectField(o);
            result = methodProxy.invokeSuper(o, objects);
            proxies.forEach(proxy -> {
                try {
                    proxy.doAfter(method);
                } catch (ProxyException e) {
                    logger.error("代理后置处理失败", e);
                }
            });
        } catch (Exception e) {
            proxies.forEach(proxy -> {
                try {
                    proxy.doThrowable(method, e);
                } catch (ProxyException e1) {
                    logger.error("代理异常处理失败", e1);
                }
            });
        } finally {
            proxies.forEach(proxy -> proxy.doFinally(method));
        }
        return result;
    }

    /**
     * 对于需要注入的属性进行注入
     *
     * @param instance 代理类实例
     */
    private void injectField(Object instance) {
        Field[] fields = instance.getClass().getDeclaredFields();
        Arrays.stream(fields).filter(field -> field.isAnnotationPresent(inject.class)).forEach(field -> {
            field.setAccessible(true);
            Object fieldValue = BeanHelper.getBean(IocHelper.findImplementClass(field.getType()));
            if (Objects.isNull(fieldValue)) {
                logger.error("{}需要注入的属性{} Bean不存在。", instance.getClass(), field.getType());
            } else {
                try {
                    field.set(fieldValue, instance.getClass().getSuperclass().cast(instance));
                } catch (IllegalAccessException e) {
                    logger.error("{}的属性{}注入{}失败", instance.getClass().getSuperclass(), field, fieldValue);
                }
            }
        });
    }


}
